package com.heima.分布式锁;


import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;


/**
 * @author 陈辉
 * @data 2023 11:22
 */
@RestController
public class StockController {

    @Autowired
    private StringRedisTemplate template;
/*    @Autowired
    private LockMapper lockMapper;*/
    @Autowired
    private RedissonClient redissonClient;

    /**
     * synchronized在分布式环境下，无法保证数据安全，因为多服务器间，不能形成资源互斥。
     *
     * @return
     */
    @GetMapping("/stock1")
    public String stock01() {     //高并发   --> 性能测试/压力测试

        synchronized ("lock") {
            //模拟：下单扣库存操作
            String stockStr = template.opsForValue().get("stock");
            int stock = Integer.parseInt(stockStr);

            if (stock > 0) {
                stock--;
                System.out.println("库存扣减成功，剩余库存：" + stock);
                template.opsForValue().set("stock", stock + "");
            } else {
                System.out.println("库存不足，下单失败!");
            }
        }
        return "ok";
    }


    /**
     * 基于mysql实现分布式锁
     * 原理：多个服务器访问同一个数据库
     * 非阻塞锁
     *
     * @return
     */
/*    @GetMapping("/stock2")
    public String stock02() {     //高并发   --> 性能测试/压力测试
        Lock lock = Lock.builder()
                .lock("lock")
                .threadId(String.valueOf(Thread.currentThread().getId()))
                .build();
        try {

            lockMapper.insert(lock);

            //模拟：下单扣库存操作
            String stockStr = template.opsForValue().get("stock");
            int stock = Integer.parseInt(stockStr);

            if (stock > 0) {
                stock--;
                System.out.println("库存扣减成功，剩余库存：" + stock);
                template.opsForValue().set("stock", stock + "");
            } else {
                System.out.println("库存不足，下单失败!");
            }

            //释放锁
            lockMapper.deleteById(lock.getId());

            return "ok";
        } catch (Exception e) {
            System.out.println("获取分布式锁失败!");
            return "error";
        }

    }*/

    /**
     * 基于mysql实现分布式锁
     * 原理：多个服务器访问同一个数据库
     * 阻塞锁
     *
     * @return
     */
/*
    @GetMapping("/stock3")
    public String stock03() throws InterruptedException {     //高并发   --> 性能测试/压力测试
        Lock lock = Lock.builder()
                .lock("lock")
                .threadId(String.valueOf(Thread.currentThread().getId()))
                .build();

        for (int i = 1; i <= 3; i++) {
            //尝试获取分布式锁  -- 加锁
            try {
                lockMapper.insert(lock);
                break;
            } catch (Exception e) {
                if (i == 3){
                    System.out.println("下单失败：获取锁失败!");
                    return "error";
                }
                Thread.sleep(1000);
            }
        }


        //模拟：下单扣库存操作
        String stockStr = template.opsForValue().get("stock");
        int stock = Integer.parseInt(stockStr);

        if (stock > 0) {
            stock--;
            System.out.println("库存扣减成功，剩余库存：" + stock);
            template.opsForValue().set("stock", stock + "");
        } else {
            System.out.println("库存不足，下单失败!");
        }

        //释放锁
        lockMapper.deleteById(lock.getId());

        return "ok";


    }
*/


    /**
     * 基于redis实现分布式锁
     * 原理：多个服务器访问同一个redis
     * 非阻塞锁
     *
     * @return
     */
    @GetMapping("/stock4")
    public String stock04() {     //高并发   --> 性能测试/压力测试
        try {
            //往redis中插入数据：能插入成功则认为是获得了分布式锁
            //往redis中插入数据：不能插入成功则认为是获得分布式锁失败
            boolean res = template.opsForValue().setIfAbsent("lock", String.valueOf(Thread.currentThread().getId()));

            if (res) {
                //模拟：下单扣库存操作
                String stockStr = template.opsForValue().get("stock");
                int stock = Integer.parseInt(stockStr);

                //模拟业务操作过程中出现问题...
                int a = 2 / 0;

                if (stock > 0) {
                    stock--;
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                    template.opsForValue().set("stock", stock + "");
                } else {
                    System.out.println("库存不足，下单失败!");
                }

                //释放锁
                template.delete("lock");

            }else{
                System.out.println("下单失败：未获得分布式锁!");
            }

            return "ok";
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return "error";
        }

    }


    /**
     * 基于redis实现分布式锁
     * 原理：多个服务器访问同一个redis
     *
     * @return
     */
    @GetMapping("/stock5")
    public String stock05() {     //高并发   --> 性能测试/压力测试
        try {
            //往redis中插入数据：能插入成功则认为是获得了分布式锁
            //往redis中插入数据：不能插入成功则认为是获得分布式锁失败
            boolean res = template.opsForValue().setIfAbsent("lock", String.valueOf(Thread.currentThread().getId()));

            if (res) {
                //模拟：下单扣库存操作
                String stockStr = template.opsForValue().get("stock");
                int stock = Integer.parseInt(stockStr);

                //模拟业务操作过程中出现问题...
                if (Thread.currentThread().getId() % 2 == 0){
                    throw new RuntimeException("业务处理出现异常....");
                }

                if (stock > 0) {
                    stock--;
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                    template.opsForValue().set("stock", stock + "");
                } else {
                    System.out.println("库存不足，下单失败!");
                }
            }else{
                System.out.println("下单失败：未获得分布式锁!");
            }
            return "ok";
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return "error";
        }finally {

            //释放锁
            String threadId = template.opsForValue().get("lock");
            if (!StringUtils.isEmpty(threadId) && threadId.equals(String.valueOf(Thread.currentThread().getId()))) {
                template.delete("lock");
            }
        }

    }


    /**
     * 基于redis实现分布式锁
     * 原理：多个服务器访问同一个redis
     * 设置锁的自动过期，避免锁被某些线程长期占用导致资源消耗
     *
     * @return
     */
    @GetMapping("/stock6")
    public String stock06() {     //高并发   --> 性能测试/压力测试
        try {
            //往redis中插入数据：能插入成功则认为是获得了分布式锁
            //往redis中插入数据：不能插入成功则认为是获得分布式锁失败
            boolean res = template.opsForValue().setIfAbsent("lock", String.valueOf(Thread.currentThread().getId()),10L, TimeUnit.SECONDS);

            if (res) {
                //模拟：下单扣库存操作
                String stockStr = template.opsForValue().get("stock");
                int stock = Integer.parseInt(stockStr);

                //模拟业务操作过程中出现问题...
                if (Thread.currentThread().getId() % 2 == 0){
                    Thread.sleep(100000);
                }

                if (stock > 0) {
                    stock--;
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                    template.opsForValue().set("stock", stock + "");
                } else {
                    System.out.println("库存不足，下单失败!");
                }
            }else{
                System.out.println("下单失败：未获得分布式锁!");
            }
            return "ok";
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return "error";
        }finally {

            //释放锁
            String threadId = template.opsForValue().get("lock");
            if (!StringUtils.isEmpty(threadId) && threadId.equals(String.valueOf(Thread.currentThread().getId()))) {
                template.delete("lock");
            }
        }

    }


    /**
     * 使用redisson框架实现分布式锁
     * 原理：多个服务器访问同一个redis
     * 设置锁的自动过期，避免锁被某些线程长期占用导致资源消耗
     *
     * @return
     */
    @GetMapping("/stock7")
    public String stock07() throws InterruptedException {     //高并发   --> 性能测试/压力测试
        //使用redisson框架实现分布式锁
        RLock lock = redissonClient.getLock("lock");
        boolean res = lock.tryLock();

        try {
            if (res) {
                //模拟：下单扣库存操作
                String stockStr = template.opsForValue().get("stock");
                int stock = Integer.parseInt(stockStr);

                //模拟业务操作过程中出现问题...
                if (Thread.currentThread().getId() % 2 == 0){
                    Thread.sleep(100000);
                }

                if (stock > 0) {
                    stock--;
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                    template.opsForValue().set("stock", stock + "");
                } else {
                    System.out.println("库存不足，下单失败!");
                }
            }else{
                System.out.println("下单失败：未获得分布式锁!");
                return "error";
            }
            return "ok";
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return "error";
        }finally {
            //释放锁
            lock.unlock();
        }

    }
}
